Creating JSF 2.2 Custom Component Dynamically - jsf

I've written (well, modified) a custom component for radio buttons appearing inside a datatable based on this article. I've modified it slightly for JSF 2.2 by using the #FacesRenderer and #FacesComponent tags and omitting the custom tag class. Everything works fine in my XHTML page
xmlns:custom="http://mynamespace"
...
<h:dataTable id="mySampleTable3"
value="#{myBackingBean.sampleTable3}"
var="item"
width="100%"
border="1"
first="0">
<f:facet name="header">
<h:panelGrid id="gridHeader3" columns="2" width="100%" >
<h:outputText id="outputText3"
value="Radios grouped in row(With our custom tag)"/>
</h:panelGrid>
</f:facet>
<h:column>
<f:facet name="header">
<h:outputText value="Emp Name" />
</f:facet>
<h:outputText id="empName" value="#{item.empName}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Excellent" />
</f:facet>
<custom:customradio id="myRadioId2"
name="myRadioRow"
value="#{item.radAns}"
itemValue="E" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Good" />
</f:facet>
<custom:customradio id="myRadioId3"
name="myRadioRow"
value="#{item.radAns}"
itemValue="G" />
</h:column>
...
</h:dataTable>
and the datatable of radio buttons work correctly (they are grouped by row). The problem arises when I use this component dynamically. So I have
HtmlPanelGrid radioGrid = (HtmlPanelGrid) getApplication()
.createComponent(HtmlPanelGrid.COMPONENT_TYPE);
...
UICustomSelectOneRadio customSelectOneRadio = (UICustomSelectOneRadio) getApplication().
createComponent(UICustomSelectOneRadio.COMPONENT_TYPE);
customSelectOneRadio.setId("rb_" + question.getQuestionId() + "_" + row + "_" + col);
customSelectOneRadio.setName("myRadioRow");
String binding = "#{" + beanName + "." + propName +
"[\"" + question.getQuestionId().toString() + "_" +
subQuestion.getId() + "\"]}";
ValueExpression ve = getExpressionFactory().
createValueExpression(getELContext(), binding, String.class);
customSelectOneRadio.setValueExpression("value", ve);
customSelectOneRadio.setItemValue(value);
radioGrid.getChildren().add(customSelectOneRadio);
Unfortunately, although the datatable is rendered, the radio buttons aren't. Blank columns appear. I was wondering if anyone knows if there is some method I should be overriding in my renderer. Mine looks like:
#FacesRenderer(componentFamily = UICustomSelectOneRadio.COMPONENT_FAMILY, rendererType = HTMLCustomSelectOneRadioRenderer.RENDERER_TYPE)
public class HTMLCustomSelectOneRadioRenderer extends Renderer {
public static final String RENDERER_TYPE = "com.myapp.jsf.renderers.HTMLCustomSelectOneRadioRenderer";
#Override
public void decode(FacesContext context, UIComponent component) {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
UICustomSelectOneRadio customSelectOneRadio;
if (component instanceof UICustomSelectOneRadio) {
customSelectOneRadio = (UICustomSelectOneRadio) component;
} else {
return;
}
Map map = context.getExternalContext().getRequestParameterMap();
String name = getName(customSelectOneRadio, context);
if (map.containsKey(name)) {
String value = (String) map.get(name);
if (value != null) {
setSubmittedValue(component, value);
}
}
}
private void setSubmittedValue(UIComponent component, Object obj) {
if (component instanceof UIInput) {
((UIInput) component).setSubmittedValue(obj);
}
}
private String getName(UICustomSelectOneRadio customSelectOneRadio,
FacesContext context) {
UIComponent parentUIComponent
= getParentDataTableFromHierarchy(customSelectOneRadio);
if (parentUIComponent == null) {
return customSelectOneRadio.getClientId(context);
} else {
if (customSelectOneRadio.getOverrideName() != null
&& customSelectOneRadio.getOverrideName().equals("true")) {
return customSelectOneRadio.getName();
} else {
String id = customSelectOneRadio.getClientId(context);
int lastIndexOfColon = id.lastIndexOf(":");
String partName = "";
if (lastIndexOfColon != -1) {
partName = id.substring(0, lastIndexOfColon + 1);
if (customSelectOneRadio.getName() == null) {
partName = partName + "generatedRad";
} else {
partName = partName + customSelectOneRadio.getName();
}
}
return partName;
}
}
}
private UIComponent getParentDataTableFromHierarchy(UIComponent uiComponent) {
if (uiComponent == null) {
return null;
}
if (uiComponent instanceof UIData) {
return uiComponent;
} else {
//try to find recursively in the Component tree hierarchy
return getParentDataTableFromHierarchy(uiComponent.getParent());
}
}
#Override
public void encodeEnd(FacesContext context, UIComponent component)
throws IOException {
if ((context == null) || (component == null)) {
throw new NullPointerException();
}
UICustomSelectOneRadio customSelectOneRadio
= (UICustomSelectOneRadio) component;
if (component.isRendered()) {
ResponseWriter writer = context.getResponseWriter();
writer.startElement("input", component);
writer.writeAttribute("type", "radio", "type");
writer.writeAttribute("id", component.getClientId(context), "id");
writer.writeAttribute("name", getName(customSelectOneRadio, context), "name");
if (customSelectOneRadio.getStyleClass() != null && customSelectOneRadio.getStyleClass().trim().
length() > 0) {
writer.writeAttribute("class", customSelectOneRadio.getStyleClass().trim(), "class");
}
if (customSelectOneRadio.getStyle() != null && customSelectOneRadio.getStyle().trim().length() > 0) {
writer.writeAttribute("style", customSelectOneRadio.getStyle().trim(), "style");
}
...
if (customSelectOneRadio.getValue() != null
&& customSelectOneRadio.getValue().equals(customSelectOneRadio.getItemValue())) {
writer.writeAttribute("checked", "checked", "checked");
}
if (customSelectOneRadio.getItemLabel() != null) {
writer.write(customSelectOneRadio.getItemLabel());
}
writer.endElement("input");
}
}
}
To quickly summarise, this is working when I use a custom tag in an facelets/XHTML file, but not when I use it dynamically, e.g.
<h:panelGroup id="dynamicPanel" layout="block" binding="#{documentController.dynamicPanel}"/>
If anyone has any ideas on what I could be missing I'd be grateful.

Gotcha! It all revolved around changing the rendererType property. From the Renderer class above, you can see I had it set to my own, i.e.
public static final String RENDERER_TYPE = "com.myapp.jsf.renderers.HTMLCustomSelectOneRadioRenderer";
(1) I changed it to
public static final String RENDERER_TYPE = "javax.faces.Radio";
(2) I also changed my xxx.taglib.xml file to reflect this, e.g.
<namespace>http://mycomponents</namespace>
<tag>
<tag-name>customradio</tag-name>
<component>
<component-type>CustomRadio</component-type>
<renderer-type>javax.faces.Radio</renderer-type> /*<----- See Here */
</component>
</tag>
(3) But, here's the final step to note. I also had to create the following constructor in my UICustomSelectOneRadio component. It didn't work without this either.
public UICustomSelectOneRadio() {
this.setRendererType("javax.faces.Radio");
}
Remember - I extended my component from UIInput, so javax.faces.Text would have been set. Leaving my component family as "CustomRadio" was fine.
I'm still not 100% comfortable with renderer types etc., but for anyone who wants a little info, BalusC explains it well here.

Related

Columns (table body) not visible in datatable when using lazydatamodel

Using LazyDataModel does not display columns in the datatable.
I have implemented custom LazyDataModel and I have implemented load method init. However, when I pass the datamodel as a value to the datatable, the columns are not rendered/displayed. But when I pass it as a list, the columns are rendered/displayed.
My LazyDataModel class
public class SearchPmrLazyDataModel extends LazyBaseDataModel<SearchDTO> {
public SearchPmrLazyDataModel() {
}
public void setData() {
if (searchCriteria == null) {
searchCriteria = new PmrSearchCriteriaDTO();
}
setWrappedData(pmrService.searchPmr(searchCriteria, teamId));
}
#Override
public List<PmrSearchResultViewDTO> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, Object> filters) {
setData();
searchCriteria.setPageRequest(determinePage(first, pageSize));
List<PmrSearchResultViewDTO> data = this.cachedData;
// rowCount
int dataSize = data.size();
this.setRowCount(dataSize);
// paginate
if (dataSize > pageSize) {
try {
return data.subList(first, first + pageSize);
} catch (IndexOutOfBoundsException e) {
return data.subList(first, first + (dataSize % pageSize));
}
} else {
return data;
}
}
#Override
public void setRowIndex(int rowIndex) {
if (rowIndex == -1 || getPageSize() == 0) {
super.setRowIndex(-1);
}
else
super.setRowIndex(rowIndex % getPageSize());
}
#Override
public Object getRowKey(PmrSearchResultViewDTO obj) {
return obj.getId();
}
}
My bean
public LazyDataModel<SearchDTO> getCorresLazyDataModel(String selectedTabName, String selectedStateName, String selectedStateName2) {
//returns corresponding lazy data model according to a business logic
return getLazyDataModel(selectedTabName, selectedStateName, selectedStateName2);
}
My XHTML page
<p:dataTable
value="#{formBean.getCorresLazyDataModel(pmrStatusTab, pmrState, pmrState2)}"
var="pmr" id="pmrStatusResultTable#{pmrStatusTab}#{pmrState}"
widgetVar="pmrStatusResultTable#{pmrStatusTab}#{pmrState}"
style="overflow: auto; width: auto! important; table-width:fixed"
rows="10" rowKey="#{pmr.id}" rowStyleClass="odd, even"
reflow="true" scrollWidth="#{formBean.scrollWidth}"
scrollable="true" scrollRows="10" paginator="true" lazy="true"
selectionMode="single" selection="#{formBean.selectedPmr}">
<p:ajax event="rowSelect" process="#this"
listener="#{formBean.onRowSelect}" />
<p:columns value="#{formBean.columns}" var="column"
columnIndexVar="colIndex" sortBy="#{pmr[column.property]}"
sortable="true">
<f:facet name="header">
<h:outputText value="#{column.header}" />
</f:facet>
<h:outputText value="#{pmr[column.property]}" />
</p:columns>
</p:dataTable>
The columns are not rendered/displayed only if I pass datamodel directly. If I pass the value as list, they are displayed, is there anything which I am missing?

JSF - "Value is not a valid option" for unknown reason >> To re-evaluate 'duplicate' [duplicate]

This question already has answers here:
Validation Error: Value is not valid
(3 answers)
Closed 6 years ago.
In my JSF application using RichFaces, I have a screen with rich:dataTable
that displays my objects, and a column that refers to another page, with the details of the selected record so that it can be used.
In this second page, after completing the requested data, when running submit, the validation returns the message:
:Value is not a valid option.
Look that, unlike the problem cited in this post, the name of the problem field is not being displayed.
During debugging, I noticed that in the selecionarEmitente() method, the object localidade is null. If I made the set on the dataTable page and the name of the selected object appears on the secondPage, what is missing?
I already researched other posts about this problem. For example, according to this post, my javabeans involved have the equals() and hashCode() methods. Unlike this other post, I'm using custom converters.
Where is my mistake?
I'm using RichFaces 4.5.1.Final, MyFaces 2.2.10, Spring 3.1.1 .
dataTable.xhtml
<h:selectOneMenu id="estado"
immediate="true" validator="#{validadorMB.validarEstado}"
converter="estadoConverter">
<f:selectItem itemLabel="---" />
<f:selectItems value="#{localidadeMB.estados}"
var="est" itemValue="#{est}" itemLabel="#{est.uf}" />
<a4j:ajax event="change" actionListener="#{localidadeMB.filtrarUF}" render="table" />
</h:selectOneMenu>
<a4j:region id="region" immediate="true">
<rich:dataTable id="tabela"
value="#{localidadeMB.getLocalidadesPorUF()}" var="loc"
rowKeyVar="row" rows="20" width="800px" render="scroller">
<rich:column id="col_localidade" sortBy="#{loc.nome}" filterBy="#{loc.nome}">
<h:outputText id="nomeLocalidade" value="#{loc.nome}" />
</rich:column>
...
<rich:column>
<h:commandLink id="declaracaolink" action="#{localidadeMB.carregarLocalidade}">
<h:graphicImage url="/img/declaracao.png" />
<f:setPropertyActionListener value="#{loc}" target="#{localidadeMB.localidade}" />
</h:commandLink>
</rich:column>
</rich:dataTable>
</a4j:region>
secondPage.xhtml
<h:body>
<h:form id="formDeclaracao" focus="estado">
<h:panelGrid columns="2" columnClasses="labelFormulario">
<h:outputLabel value="UF" for="estado" />
<h:selectOneMenu id="estado"
value="#{localidadeMB.localidade.estado}"
label="#{localidadeMB.localidade.estado.uf}"
valueChangeListener="#{localidadeMB.selecionarEstado}"
validator="#{validadorMB.validarEstado}" immediate="true"
converter="estadoConverter"
style="width: 45px;" styleClass="listbox">
<f:selectItems value="#{localidadeMB.estados}"
var="est" itemValue="#{est}" itemLabel="#{est.uf}" />
<a4j:ajax event="change" render="nomeLocalidade" />
</h:selectOneMenu>
<h:outputLabel value="Nome da localidade:" for="nomeLocalidade" />
<h:selectOneMenu id="nomeLocalidade"
value="#{localidadeMB.localidade}"
label="#{localidadeMB.localidade.nome}" immediate="true"
valueChangeListener="#{localidadeMB.selecionarLocalidade}"
validator="#{validadorMB.validarLocalidade}"
converter="localidadeConverter"
style="width: 220px;" styleClass="listbox">
<f:selectItems value="#{localidadeMB.localidades}"
var="loc" itemValue="#{loc}" itemLabel="#{loc.nome}" />
</h:selectOneMenu>
<h:outputLabel value="Interessado:" for="interessado" />
<h:inputText id="interessado" value="" required="true"
requiredMessage="xxxxxxxxx" immediate="true"
styleClass="listbox" style="width: 215px;">
</h:inputText>
<h:outputLabel value="Solicitante:" for="solicitante" />
<h:inputText id="solicitante" value="" required="true"
requiredMessage="xxxxxxxxxxxxx" immediate="true">
</h:inputText>
<h:outputLabel value="Documento apresentado:" for="documento" />
<h:inputText id="documento" value="" required="true"
requiredMessage="xxxxxxxxxxxxx" immediate="true">
</h:inputText>
<h:outputLabel value="Emitente:" for="emitente" />
<h:selectOneMenu id="emitente"
value="#{localidadeMB.emitente}"
label="#{localidadeMB.emitente.descricaoReduzida}"
valueChangeListener="#{localidadeMB.selecionarEmitente}"
validator="#{validadorMB.validarEmitente}"
converter="emitenteConverter">
<f:selectItem itemLabel="---" />
<f:selectItems value="#{localidadeMB.emitentes}" var="em" itemValue="#{em}" itemLabel="#{em.descricaoReduzida}" />
</h:selectOneMenu>
<h:commandButton id="btnGerarDeclaracao" value="Get PDF"
action="#{localidadeMB.getPDF()}" style="width: 84px;" />
</h:panelGrid>
</h:form>
</h:body>
ManagedBean
#ManagedBean
public class LocalidadeMB{
private Emitente emitente;
private Estado estado;
private Localidade localidade;
public String carregarLocalidade() {
estado = localidade.getEstado();
localidade = getLocalidade();
getLocalidades();
carregarLocalidadePorUF(estado);
return "secondPage.xhtml";
}
public void filtrarUF(ActionEvent action) {
try {
String uf = JSFHelper.getRequestParameter("formConsulta"
+ UINamingContainer.getSeparatorChar(JSFHelper
.getFacesContext()) + "estado");
estado = estadoFacade.getEstado(Integer.parseInt(uf));
} catch (Exception e) {
...
}
}
public List<Emitente> getEmitentes() {
try {
return emitenteFacade.getEmitentes();
} catch (Exception e) {
...
}
}
public List<Emitente> getEstados() {
try {
return estadoFacade.getEstados();
} catch (...) {
...
}
}
public List<Localidade> getLocalidades() {
try {
return localidadeFacade.getLocalidades();
} catch (Exception e) {
...
}
}
public List<Localidade> getLocalidadesPorUF() {
try {
String uf = JSFHelper.getRequestParameter("formConsulta"
+ UINamingContainer.getSeparatorChar(JSFHelper
.getFacesContext()) + "estado");
if ((uf != null) && (!uf.equals("---"))) {
estado = estadoFacade.getEstado(Integer.parseInt(uf));
return localidadeFacade.getLocalidadesPorEstado(estado);
} else {
return null;
}
} catch (Exception e) {
...
}
}
public void selecionarEmitente(ValueChangeEvent evento) {
emitente = (Emitente)evento.getNewValue();
}
public void selecionarEstado(ValueChangeEvent evento) {
estado = (Estado)evento.getNewValue();
}
public void selecionarLocalidade(ValueChangeEvent evento) {
localidade = (Localidade)evento.getNewValue();
}
}
Validator
#ManagedBean(name = "validadorMB")
public class ValidadorLocalidadeMB {
public void validarEmitente(FacesContext context, UIComponent componentToValidate, Object value) throws ValidatorException {
if (((Emitente) value).getEmitenteId() == 0) {
FacesMessage msg = new FacesMessage(null, "Selecione o emitente");
throw new ValidatorException(msg);
}
}
public void validarEstado(FacesContext context, UIComponent componentToValidate, Object value) throws ValidatorException {
if (((Estado) value).getEstadoId() == 0) {
FacesMessage msg = new FacesMessage(null, "Selecione uma UF");
throw new ValidatorException(msg);
}
}
public void validarLocalidade(FacesContext context, UIComponent componentToValidate, Object value) throws ValidatorException {
if (((Localidade) value).getLocalidadeId() == 0) {
FacesMessage msg = new FacesMessage(null, "Selecione uma localidade");
throw new ValidatorException(msg);
}
}
}
Converters
#FacesConverter(value = "emitenteConverter")
public class EmitenteConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent ui, String value) throws ConverterException {
ValueExpression vex = context
.getApplication()
.getExpressionFactory()
.createValueExpression(context.getELContext(),
"#{emitenteFacade}", EmitenteFacadeImpl.class);
EmitenteFacadeImpl fac = (EmitenteFacadeImpl) vex.getValue(context
.getELContext());
try {
return fac.getEmitentePorId(Integer.valueOf(value));
} catch (NumberFormatException | DAOException e) {
....
}
}
#Override
public String getAsString(FacesContext context, UIComponent ui, Object value) throws ConverterException {
if (value == null) {
return "";
}
if (value instanceof Emitente) {
return String.valueOf(((Emitente) value).getEmitenteId());
}
}
}
#FacesConverter(value = "estadoConverter")
public class EstadoConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent ui, String value) throws ConverterException {
ValueExpression vex = context
.getApplication()
.getExpressionFactory()
.createValueExpression(context.getELContext(),
"#{estadoFacade}", EstadoFacadeImpl.class);
EstadoFacadeImpl fac = (EstadoFacadeImpl) vex.getValue(context
.getELContext());
try {
return fac.getEstadoPorId(Integer.valueOf(value));
} catch (NumberFormatException | DAOException e) {
....
}
}
#Override
public String getAsString(FacesContext context, UIComponent ui, Object value) throws ConverterException {
if (value == null) {
return "";
}
if (value instanceof Estado) {
return String.valueOf(((Estado) value).getEstadoId());
}
}
}
#FacesConverter(value = "localidadeConverter")
public class LocalidadeConverter implements Converter {
#Override
public Object getAsObject(FacesContext context, UIComponent ui, String value) throws ConverterException {
ValueExpression vex = context
.getApplication()
.getExpressionFactory()
.createValueExpression(context.getELContext(),
"#{localidadeFacade}", LocalidadeFacadeImpl.class);
LocalidadeFacadeImpl fac = (LocalidadeFacadeImpl) vex.getValue(context.getELContext());
try {
return fac.getLocalidadePorId(Integer.valueOf(value));
} catch (NumberFormatException | DAOException e) {
....
}
}
#Override
public String getAsString(FacesContext context, UIComponent ui, Object value) throws ConverterException {
if (value == null) {
return "";
}
if (value instanceof Localidade) {
return String.valueOf(((Localidade) value).getLocalidadeId());
}
}
}
Your problem might be with this line:
value="#{(localidadeMB.localidade.localidadeId != null) ? localidadeMB.localidade.localidadeId :localidadeMB.localidade}"
The value of a <h:selectOneMenu> should be a simple property, not an expression, as it has to set.
Edit:
There could be another problem with your code:
You construct those SelectItems with the entity's ID as the value property, and then use converter to convert String to/from the entity itself.
Thus there are a few possibilities for you:
Use the converter, and the "whole" entity as the value of SelectItem.
Or don't use Selectitems, but a List of the entities themselves, as your first link does.
The other possibility is to use a dummy entity, set its id property, and in your valueChangeListener retrieve the entity by the ID.

Primefaces Datatable converter with filtering

I have the datatable and selectonemenu inside it.
I want to show images in select one menu items.So a have Rating class and converter ratingConverter for Rating class.
I have the next problem here: filtering by this column works just once! When I try to change item, method load(..) from LazyComicsDataModel calls. But after
filtering one time nothing worked! Method load not calls anymore. There are't any exceptions in server.log. When I don't use converter and class Rating (use just String class) it works right any number of times.
I would like to get any ideas why it might be happened.
Code:
Part of xhtml page:
<p:ajax
event="rowSelect"
update=":form:comicsDetail"
oncomplete="PF('comicsDialog').show()"/>
<p:column width="20%"
headerText="#{comicsCatalogueView.columnComicsRating}"
sortBy="#{comics.rating}"
filterBy="#{comics.rating}"
filterMatchMode="exact" >
<f:facet name="filter" >
<p:selectOneMenu
value="#{comicsCatalogueView.rating}"
var="r"
onchange="PF('comicsTable').filter()"
converter="ratingConverter"
>
<f:selectItem
value="#{null}"
itemValue="#{null}" />
<f:selectItems
value="#{comicsFinderManagedBean.ratings}"
var="rating"
itemLabel="#{rating.value} - #{rating.value+1}"
itemValue="#{rating}"
/>
<p:column>
<p:graphicImage value="#{r.image}"/>
</p:column>
</p:selectOneMenu>
</f:facet>
<h:panelGroup>
<pf:rating disabled="true"
totalStars="5"
step="0.1"
value="#{comics.rating}"
>
</pf:rating>
</h:panelGroup>
</p:column>
Rating class:
public class Rating {
public Rating(Integer value, String image) {
this.value = value;
this.image = image;
}
private Integer value;
private String image;
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
#Override
public int hashCode() {
int hash = 5;
hash = 71 * hash + (this.value != null ? this.value.hashCode() : 0);
hash = 71 * hash + (this.image != null ? this.image.hashCode() : 0);
return hash;
}
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final Rating other = (Rating) obj;
if (this.value != other.value && (this.value == null || !this.value.equals(other.value))) {
return false;
}
if ((this.image == null) ? (other.image != null) : !this.image.equals(other.image)) {
return false;
}
return true;
}
#Override
public String toString() {
return "Rating{" + "value=" + value + ", image=" + image + '}';
}
Rating converter:
#FacesConverter("ratingConverter")
public class RatingConverter implements Converter {
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
ComicsFinderManagedBean ratingFinderManagedBean = (ComicsFinderManagedBean)
fc.getExternalContext().getApplicationMap().get("comicsFinderManagedBean");
return ratingFinderManagedBean.getRating(Integer.parseInt(value));
}
else {
return null;
}
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
if(object != null) {
return String.valueOf(((Rating) object).getValue());
}
else {
return null;
}
}
}

ClassCastException in selectManyMenu - Integer cannot be cast to String

The list passed to the select's value is of Integer type.
<p:selectManyMenu id="estabelecimentos" value="#{questionarioMB.estabelecimentosIds}" var="e" converter="#{estabelecimentoConverter}" style="width:100%" filter="true" filterMatchMode="contains" showCheckbox="true">
<f:selectItems value="#{questionarioMB.estabelecimentos}" var="estabelecimento" itemValue="#{estabelecimento}" itemLabel="#{estabelecimento.nomefantasia}" />
<p:column>
<h:outputText value="#{estabelecimentoMB.getIdentificadorByEstabelecimentoId(e.id)}" />
</p:column>
<p:column>
<h:outputText value="#{e.nomefantasia}" />
</p:column>
</p:selectManyMenu>
Netbeans cannot find the attributes in the outputTexts ("unknown property") and the line throwing the exception is the following:
this.estabelecimentosIds.parallelStream().forEach((Integer id) -> {
this.questionarioBean.insertQuestionarioHasEstabelecimento(this.questionarioBean.getLastId() + 1, id);
});
The converter:
#Named
public class EstabelecimentoConverter implements Converter {
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if (value != null && value.trim().length() > 0) {
try {
EstabelecimentoMB estabelecimentoMB = (EstabelecimentoMB) fc.getExternalContext().getApplicationMap().get("estabelecimentoMB");
return estabelecimentoMB.getEstabelecimentos().get(Integer.parseInt(value));
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Erro de Conversão", "Estabelecimento inválido."));
}
} else {
return null;
}
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object o) {
if (o != null) {
return String.valueOf(((Estabelecimento) o).getId());
} else {
return null;
}
}
}
P.S.: I can't use the field tradingName because it can be repeated in table establishment, so I must use the "id" to differentiate them. The first column has the identifier for that establishment (in another table, "client_has_establishment", and can be repeated as well - but not for the same client_id).
Based on BalusC's answer here: https://stackoverflow.com/a/13866179/1639141
Changed the converter to JSF builtin IntegerConverter:
<p:selectManyMenu ... converter="javax.faces.Integer">

Unable to find matching navigation case with from-view-id '/index.xhtml' for action : JSF

I am getting the following error :
Unable to find matching navigation case with from-view-id '/index.xhtml' for action '#{medcontroller.getMedGeneric}' with outcome 'javax.faces.model.ListDataModel#7a652236'
I am new to jsf and I'm really clueless about solving this error. I have a ManagedBean with the following code:
MedController.java
#ManagedBean(name = "medcontroller")
#SessionScoped
public class MedController implements Serializable {
int startId;
String gName;
int endId;
DataModel medNames;
//DataModel medGeneric;
MedicineHelper helper;
private int recordCount = 1000;
private int pageSize = 10;
private Medicine current;
private int selectedItemIndex;
public MedController() {
helper = new MedicineHelper();
startId = 1;
endId = 10;
}
public MedController(int startId, int endId) {
helper = new MedicineHelper();
this.startId = startId;
this.endId = endId;
}
public Medicine getSelected() {
if (current == null) {
current = new Medicine();
selectedItemIndex = -1;
}
return current;
}
public DataModel getMedNames() {
if (medNames == null) {
medNames = new ListDataModel(helper.getMedNames(startId, endId));
}
return medNames;
}
public String getgName()
{
return gName;
}
public void setgName(String gName)
{
this.gName = gName;
}
public DataModel getMedGeneric() {
if (medNames == null) {
medNames= new ListDataModel(helper.getMedGeneric(gName));
}
return medNames;
}
void recreateModel() {
medNames = null;
}
public boolean isHasNextPage() {
if (endId + pageSize <= recordCount) {
return true;
}
return false;
}
public boolean isHasPreviousPage() {
if (startId-pageSize > 0) {
return true;
}
return false;
}
public String next() {
startId = endId+1;
endId = endId + pageSize;
recreateModel();
return "index";
}
public String previous() {
startId = startId - pageSize;
endId = endId - pageSize;
recreateModel();
return "index";
}
public int getPageSize() {
return pageSize;
}
public String prepareView(){
current = (Medicine) getMedNames().getRowData();
return "browse";
}
public String prepareList(){
recreateModel();
return "index";
}
}
And here is my JSF file
index.xhtml
<ui:define name="body">
<h:form styleClass="jsfcrud_list_form">
<h:commandLink action="#{medcontroller.previous}" value="Previous #{medcontroller.pageSize}" rendered="#{medcontroller.hasPreviousPage}"/>
<h:commandLink action="#{medcontroller.next}" value="Next #{medcontroller.pageSize}" rendered="#{medcontroller.hasNextPage}"/>
<h:dataTable value="#{medcontroller.medNames}" var="item" border="1" cellpadding="15" cellspacing="10" rowClasses="jsfcrud_odd_row,jsfcrud_even_row" rules="all" style="border:solid 1px">
<h:column>
<f:facet name="header">
<h:outputText value="BrandName"/>
</f:facet>
<h:outputText value="#{item.brandName}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Generic"/>
</f:facet>
<h:outputText value="#{item.generic}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value=" "/>
</f:facet>
<h:commandLink action="#{medcontroller.prepareView}" value="View"/>
</h:column>
</h:dataTable>
<h:inputText value="#{medcontroller.gName}" />
<h:commandButton value="Submit" action="#{medcontroller.getMedGeneric}" />
</h:form>
</ui:define>
Please help me solve the error.
Also, I do not have a faces-config.xml file. I am using netbeans ide 7.1.2 web application with jsf and hibernate framework.
Thank you in advance.
The <h:commandButton action> must point to a method which invokes some business logic and returns either void or a String representing the target page you'd like to (re)display. However you returned a whole ListDataModel which isn't making any sense to JSF navigation handler and hence this error.
Something like this should do:
public String getMedGeneric(){
// Do your business logic here.
return "someViewId";
}
This will navigate to someViewId.xhtml. However, if you intend to stick on the same view, just let it return void (or null) and it will redisplay the same view.
public void getMedGeneric(){
// Do your business logic here.
}
By the way, it's really a poor naming convention to prefix action method names with get. This is confusing and makes your code not self-documenting. Rather name it loadMedGeneric() or so. I'm merely guessing as you didn't tell anywhere about the concrete functional requirement, what exactly that button should be doing.
getMedGeneric should return java.lang.String which represent navigation to another page described in faces-config.xml. In your case it return some model so it will not work unfortunatell. Let try to put getMedGeneric() action to actionListener and then in action put navigation String. i.e:
action="navString" actionListener="#{medcontroller.getMedGeneric}"
you should try just
actionListener="#{medcontroller.getMedGeneric}"

Resources