I have a project which consist in a treeview of a folder.
I decided to create a custom component.
The XHTML looks great, I can get the request param of the selected files on the decode method.
The problem is: I can't obtain the value on a managedBean attribute (see submit method)...
here is my code, I don't understand what is wrong or I miss...
Custom Component
#FacesComponent(createTag = true, tagName = "indexComponent", namespace = "http://index.com/tags")
public class IndexComponent extends UISelectMany{
private String pathString = "/home/test/";
private List<Path> filesNames = new ArrayList<>();
private static String NAME_CHKBOX = "checkbox";
IndexComponent instance = this;
public IndexComponent getInstance() {
return instance;
}
public void setInstance(IndexComponent instance) {
this.instance = instance;
}
#Override
public String getFamily() {
return "treeview";
}
#Override
public void encodeBegin(FacesContext context) throws IOException{
ResponseWriter writer = context.getResponseWriter();
writer.startElement("ul", this);
writer.writeAttribute("data-role", "treeview", null);
writer.writeAttribute("id", this.getClientId(context), null);
Files.walkFileTree(Paths.get(pathString), new HashSet<>(),2, new FileVisitor<Path>() {
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
filesNames.add(dir);
writer.startElement("li", instance);
writer.writeAttribute("data-caption", dir.getFileName() , null);
writer.writeAttribute("class", "" , null);
writer.startElement("ul", instance);
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
filesNames.add(file);
writer.startElement("li", instance);
writer.startElement("input", instance);
writer.writeAttribute("type", "checkbox" , null);
writer.writeAttribute("name", getHtmlNameCheckBox(context) , null);
writer.writeAttribute("value", file.getFileName() , null);
writer.writeAttribute("data-role", "checkbox" , null);
writer.writeAttribute("data-caption", file.getFileName() , null);
writer.writeAttribute("title", "" , null);
writer.endElement("input");
writer.endElement("li");
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
writer.endElement("li");
writer.endElement("ul");
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
writer.startElement("li", instance);
writer.writeAttribute("data-caption", file.getFileName() , null);
writer.endElement("li");
return FileVisitResult.CONTINUE;
}
});
writer.endElement("ul");
instance.encodeEnd(context);
}
public List<Path> getFilesNames() {
return filesNames;
}
public void setFilesNames(List<Path> filesNames) {
this.filesNames = filesNames;
}
#Override
public void decode(FacesContext context) {
String[] result = context.getExternalContext().getRequestParameterValuesMap().get( getHtmlNameCheckBox(context) );
if(result != null && result.length > 0) {
List<String> liste = Arrays.asList( result );
this.setSubmittedValue(liste);
}
}
private String getHtmlNameCheckBox(FacesContext context){
return String.format("%s:%s", this.getClientId(context), NAME_CHKBOX);
}
}
ManadgeBean
#Named
#ViewScoped
public class IndexBean implements Serializable{
private static final long serialVersionUID = -1L;
private List<String> filesNames;
#PostConstruct
public void init() {
filesNames = new ArrayList<>();
}
public void submit() {
LogUtils.info("submit !!!");
for (String s : filesNames) {
LogUtils.info(s);
}
}
public List<String> getFilesNames() {
return filesNames;
}
public void setFilesNames(List<String> filesNames) {
this.filesNames = filesNames;
}
}
XHTML
<!DOCTYPE html>
<h:html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:p="http://primefaces.org/ui"
xmlns:t="http://index.com/tags"
xmlns:composite="http://java.sun.com/jsf/composite">
<h:head>
<h:outputStylesheet name="metro-all.css" library="css" />
<h:outputScript name="jquery-3.3.1.min.js" library="js" />
<h:outputScript name="metro.js" library="js" />
</h:head>
<h:body>
<h:form>
<t:indexComponent value="#{indexBean.filesNames}" />
<h:commandButton actionListener="#{indexBean.submit()}" type="submit" styleClass="button primary" value="Submit" />
</h:form>
</h:body>
</h:html>
Note : I don't want to use Primefaces or other framework.
Related
How do I define a jsf composite component properly such that its value gets bean-validated correctly in the case it contains a collection?
We have an entity that references a collection of details. Both are annotated with bean-validation-constraints. Please note the annotations at the details-property.
public class Entity implements Serializable {
#NotEmpty
private String name;
#NotEmpty
#UniqueCategory(message="category must be unique")
private List<#Valid Detail> details;
/* getters/setters */
}
public class Detail implements Serializable {
#Pattern(regexp="^[A-Z]+$")
private String text;
#NotNull
private Category category;
/* getters/setters */
}
public class Category implements Serializable {
private final int id;
private final String description;
Category(int id, String description) {
this.id = id;
this.description = description;
}
/* getters/setters */
}
public class MyConstraints {
#Target({ ElementType.TYPE, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = UniqueCategoryValidator.class)
#Documented
public static #interface UniqueCategory {
String message();
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public static class UniqueCategoryValidator implements ConstraintValidator<UniqueCategory, Collection<Detail>> {
#Override
public boolean isValid(Collection<Detail> collection, ConstraintValidatorContext context) {
if ( collection==null || collection.isEmpty() ) {
return true;
}
Set<Category> set = new HashSet<>();
collection.forEach( d-> set.add( d.getCategory() ));
return set.size() == collection.size();
}
public void initialize(UniqueCategory constraintAnnotation) {
// intentionally empty
}
}
private MyConstraints() {
// only static stuff
}
}
The entity can be edited in a jsf-form, where all the tasks concerning the details are encapsulated in a composite component, eg
<h:form id="entityForm">
<h:panelGrid columns="3">
<p:outputLabel for="#next" value="name"/>
<p:inputText id="name" value="#{entityUiController.entity.name}"/>
<p:message for="name"/>
<p:outputLabel for="#next" value="details"/>
<my:detailsComponent id="details" details="#{entityUiController.entity.details}"
addAction="#{entityUiController.addAction}"/>
<p:message for="details"/>
<f:facet name="footer">
<p:commandButton id="saveBtn" value="save"
action="#{entityUiController.saveAction}"
update="#form"/>
</f:facet>
</h:panelGrid>
</h:form>
where my:detailsComponent is defined as
<ui:component xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:cc="http://java.sun.com/jsf/composite"
xmlns:p="http://primefaces.org/ui"
>
<cc:interface>
<cc:attribute name="details" required="true" type="java.lang.Iterable"/>
<cc:attribute name="addAction" required="true" method-signature="void action()"/>
</cc:interface>
<cc:implementation>
<p:outputPanel id="detailsPanel">
<ui:repeat id="detailsContainer" var="detail" value="#{cc.attrs.details}">
<p:inputText id="text" value="#{detail.text}" />
<p:message for="text"/>
<p:selectOneMenu id="category" value="#{detail.category}"
converter="#{entityUiController.categoriesConverter}"
placeholder="please select" >
<f:selectItem noSelectionOption="true" />
<f:selectItems value="#{entityUiController.categoryItems}"/>
</p:selectOneMenu>
<p:message for="category"/>
</ui:repeat>
</p:outputPanel>
<p:commandButton id="addDetailBtn" value="add" action="#{cc.attrs.addAction}"
update="detailsPanel" partialSubmit="true" process="#this detailsPanel"/>
</cc:implementation>
</ui:component>
and the EntityUiController is
#Named
#ViewScoped
public class EntityUiController implements Serializable {
private static final Logger LOG = Logger.getLogger( EntityUiController.class.getName() );
#Inject
private CategoriesBoundary categoriesBoundary;
#Valid
private Entity entity;
#PostConstruct
public void init() {
this.entity = new Entity();
}
public Entity getEntity() {
return entity;
}
public void saveAction() {
LOG.log(Level.INFO, "saved entity: {0}", this.entity );
}
public void addAction() {
this.entity.getDetails().add( new Detail() );
}
public List<SelectItem> getCategoryItems() {
return categoriesBoundary.getCategories().stream()
.map( cat -> new SelectItem( cat, cat.getDescription() ) )
.collect( Collectors.toList() );
}
public Converter<Category> getCategoriesConverter() {
return new Converter<Category>() {
#Override
public String getAsString(FacesContext context, UIComponent component, Category value) {
return value==null ? null : Integer.toString( value.getId() );
}
#Override
public Category getAsObject(FacesContext context, UIComponent component, String value) {
if ( value==null || value.isEmpty() ) {
return null;
}
try {
return categoriesBoundary.findById( Integer.valueOf(value).intValue() );
} catch (NumberFormatException e) {
throw new ConverterException(e);
}
}
};
}
}
When we now press the save-button in the above h:form, the name-inputText is validated correctly but the #NotEmpty- and #UniqueCategory-constraint on the details-property are ignored.
What am I missing?
We are on java-ee-7, payara 4.
After diving into this a bit we endet up with a solution using a backing component ValidateListComponent. It was inspired by UIValidateWholeBean and WholeBeanValidator.
That component extends from UIInput and overrides the validation-Methods to operate on a clone of the above details-collection which gets populated with the already validated values of the children-UIInput. Seems to work for now.
<ui:component ...>
<cc:interface componentType="validatedListComponent">
<cc:attribute name="addAction" required="true" method-signature="void action()"/>
</cc:interface>
<cc:implementation>
... see above ...
</cc:implementation>
</ui:component>
with the backing component defined as
#FacesComponent(value = "validatedListComponent")
#SuppressWarnings("unchecked")
public class ValidatedListComponent extends UIInput implements NamingContainer {
#Override
public String getFamily() {
return "javax.faces.NamingContainer";
}
/**
* Override {#link UIInput#processValidators(FacesContext)} to switch the order of
* validation. First validate this components children, then validate this itself.
*/
#Override
public void processValidators(FacesContext context) {
// Skip processing if our rendered flag is false
if (!isRendered()) {
return;
}
pushComponentToEL(context, this);
for (Iterator<UIComponent> i = getFacetsAndChildren(); i.hasNext(); ) {
i.next().processValidators(context);
}
if (!isImmediate()) {
Application application = context.getApplication();
application.publishEvent(context, PreValidateEvent.class, this);
executeValidate(context);
application.publishEvent(context, PostValidateEvent.class, this);
}
popComponentFromEL(context);
}
/**
* Override {#link UIInput#validate(FacesContext)} to validate a cloned collection
* instead of the submitted value.
*
* Inspired by {#link UIValidateWholeBean} and {#link WholeBeanValidator}.
*/
#Override
public void validate(FacesContext context) {
AreDetailsValidCallback callback = new AreDetailsValidCallback();
visitTree( VisitContext.createVisitContext(context)
, callback
);
if ( callback.isDetailsValid() ) {
Collection<?> clonedValue = cloneCollectionAndSetDetailValues( context );
validateValue(context, clonedValue);
}
}
/**
* private method copied from {#link UIInput#executeValidate(FacesContext)}.
* #param context
*/
private void executeValidate(FacesContext context) {
try {
validate(context);
} catch (RuntimeException e) {
context.renderResponse();
throw e;
}
if (!isValid()) {
context.validationFailed();
context.renderResponse();
}
}
private Collection<Object> cloneCollectionAndSetDetailValues(FacesContext context) {
ValueExpression collectionVE = getValueExpression("value");
Collection<?> baseCollection = (Collection<?>) collectionVE.getValue(context.getELContext());
if ( baseCollection==null ) {
return null;
}
// Visit all the components children to find their already validated values.
FindDetailValuesCallback callback = new FindDetailValuesCallback(context);
this.visitTree( VisitContext.createVisitContext(context)
, callback
);
// Clone this components value and put in all cloned details with validated values set.
try {
Collection<Object> clonedCollection = baseCollection.getClass().newInstance();
for( Entry<Object,Map<String,Object>> entry : callback.getDetailSubmittedData().entrySet() ) {
Object clonedDetail = cloneDetailAndSetValues( entry.getKey(), entry.getValue() );
clonedCollection.add( clonedDetail );
}
return clonedCollection;
} catch ( Exception e ) {
throw new ConverterException(e);
}
}
private <T> T cloneDetailAndSetValues(T detail, Map<String, Object> propertyMap) throws Exception {
T clonedDetail = clone(detail);
// check the properties we have in the detail
Map<String, PropertyDescriptor> availableProperties = new HashMap<>();
for (PropertyDescriptor propertyDescriptor : getBeanInfo(detail.getClass()).getPropertyDescriptors()) {
availableProperties.put(propertyDescriptor.getName(), propertyDescriptor);
}
// put their value (or local value) into our clone
for (Map.Entry<String, Object> propertyToSet : propertyMap.entrySet()) {
availableProperties.get(propertyToSet.getKey()).getWriteMethod().invoke(clonedDetail,
propertyToSet.getValue());
}
return clonedDetail;
}
private static <T> T clone(T object) throws Exception {
// clone an object using serialization.
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream);
out.writeObject(object);
byte[] bytes = byteArrayOutputStream.toByteArray();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
ObjectInputStream in = new ObjectInputStream(byteArrayInputStream);
return (T) in.readObject();
}
private class FindDetailValuesCallback implements VisitCallback {
private final FacesContext context;
private final Map<Object, Map<String, Object>> detailSubmittedData = new HashMap<>();
public FindDetailValuesCallback(final FacesContext context) {
this.context = context;
}
final Map<Object, Map<String, Object>> getDetailSubmittedData() {
return detailSubmittedData;
}
#Override
public VisitResult visit(VisitContext visitContext, UIComponent component) {
if ( isVisitorTarget(component) ) {
ValueExpression ve = component.getValueExpression("value");
Object value = ((EditableValueHolder)component).getValue();
if (ve != null) {
ValueReference vr = ve.getValueReference(context.getELContext());
String prop = (String)vr.getProperty();
Object base = vr.getBase();
Map<String, Object> propertyMap
= Optional.ofNullable( detailSubmittedData.get(base) )
.orElseGet( HashMap::new );
propertyMap.put(prop, value );
detailSubmittedData.putIfAbsent( base, propertyMap);
}
}
return ACCEPT;
}
}
private class AreDetailsValidCallback implements VisitCallback {
private boolean detailsValid;
public AreDetailsValidCallback() {
this.detailsValid = true;
}
public boolean isDetailsValid() {
return detailsValid;
}
#Override
public VisitResult visit(VisitContext visitContext, UIComponent component) {
if ( isVisitorTarget(component) ) {
if ( !((EditableValueHolder)component).isValid() ) {
this.detailsValid = false;
}
}
return ACCEPT;
}
}
private boolean isVisitorTarget( UIComponent component ) {
return component instanceof EditableValueHolder && component.isRendered()
&& component!=ValidatedListComponent.this;
}
}
UPDATE: sometimes it is a problem to obtain the ValueReference in FindDetailValuesCallback#visit. The answer given by Michele here solved that problem.
I am trying to create a datatable programmatically
Here where i am making a new instance for the dataTable :
public DataTables getDataTableTest() {
if(dataTableTest==null){
String var=getDatatableVar();
dataTableTest=new DataTables(context, app, "datatable1", "width: 50%", var,
"#{programmaticallyComp.roleLists}", "single", true, 5, "", "");
}
return dataTableTest;
}
DataTables
public class DataTables extends DataTable {
private static final long serialVersionUID = 1L;
public DataTables() {
super();
// TODO Auto-generated constructor stub
}
public DataTables(FacesContext context, Application app, String id,String style,String var,String expression,
String selectionMode, boolean paginator, int row,
String rowSelectListener, String rowUnSelectListener) {
super();
// TODO Auto-generated constructor stub
app.createComponent(DataTable.COMPONENT_TYPE);
setStyle(style + " !important;");
setSelectionMode(selectionMode);
setPaginator(paginator);
setRows(row);
setVar(var);
setMethodsExpression(this, context, app, rowSelectListener, rowUnSelectListener);
setComId(this, id);
setValueExpressions(this, context, app, expression);
}
private void setComId(DataTable comp,String id){
comp.setId(id);
}
private void setValueExpressions(DataTable comp ,FacesContext context,Application app,String expression){
ELContext elContext=context.getELContext();
ValueExpression value=app.getExpressionFactory().createValueExpression(elContext,expression ,Object.class);
comp.setValueExpression("value",value);
}
private void setMethodsExpression(DataTable comp, FacesContext context,
Application app, String rowSelectListener,
String rowUnSelectListener) {
ELContext elContext = context.getELContext();
if (JSFUtils.isInputFilled(rowSelectListener)) {
MethodExpression rowSelectListenerMethod=app.getExpressionFactory()
.createMethodExpression(elContext,
rowSelectListener, null,new Class[] {SelectEvent.class});
comp.setRowSelectListener(rowSelectListenerMethod);
}
if (JSFUtils.isInputFilled(rowUnSelectListener)) {
MethodExpression rowUnSelectListenerMethod=app.getExpressionFactory()
.createMethodExpression(elContext,
rowUnSelectListener, null,new Class[] {UnselectEvent.class});
comp.setRowUnselectListener(rowUnSelectListenerMethod);
}
}
}
I am assign the related columns by this way :
getDataTableTest();
for(RoleBean roles : roleLists){
OutputLabel outputLabelId=new OutputLabel(context,app,roles.getRoles().getRoleNum());
OutputLabel outputLabelName=new OutputLabel(context,app,roles.getDescription().getDesc());
getColDataTableIdTest().getChildren().add(outputLabelId);
getColDataTableNameTest().getChildren().add(outputLabelName);
}
getDataTableTest().getChildren().add(getColDataTableIdTest());
getDataTableTest().getChildren().add(getColDataTableNameTest());
getRowDataTable().getChildren().add(getDataTableTest());
container.getChildren().add(getRowDataTable());
OutPutLabel Class
public class OutputLabel extends HtmlOutputLabel {
public OutputLabel() {
super();
// TODO Auto-generated constructor stub
}
public OutputLabel(FacesContext context, Application app, String id,
String value) {
super();
// TODO Auto-generated constructor stub
app.createComponent(HtmlOutputLabel.COMPONENT_TYPE);
compSetId(this, id);
setValue(value);
setStyleClass(JSFUtils.BootstrapClasses.LabelsControl.toString());
}
public OutputLabel(FacesContext context, Application app, Object value) {
super();
// TODO Auto-generated constructor stub
app.createComponent(HtmlOutputLabel.COMPONENT_TYPE);
setValue(value);
}
private void compSetId(UIOutput comp, String id) {
comp.setId(id);
}
getColDataTableIdTest() :
public AceColumn getColDataTableIdTest() {
if(colDataTableIdTest==null){
colDataTableIdTest=new AceColumn(app,"ID");
}
return colDataTableIdTest;
}
getColDataTableNameTest() :
public AceColumn getColDataTableNameTest() {
if(colDataTableNameTest==null){
colDataTableNameTest=new AceColumn(app,"ID");
}
return colDataTableNameTest;
}
AceColumn class :
public class AceColumn extends Column {
private static final long serialVersionUID = 1L;
public AceColumn() {
super();
// TODO Auto-generated constructor stub
}
public AceColumn(Application app,String headerText) {
super();
// TODO Auto-generated constructor stub
app.createComponent(Column.COMPONENT_TYPE);
setHeaderText(headerText);
}
getRowDataTable()
public RowContainer getRowDataTable() {
if(rowDataTable==null){
rowDataTable= new RowContainer(context, app, false,"dataTableRowId");
}
return rowDataTable;
}
RowContainer
public class RowContainer extends HtmlPanelGroup {
public RowContainer() {
super();
// TODO Auto-generated constructor stub
}
public RowContainer(FacesContext context,Application app,boolean required,String id) {
app.createComponent(HtmlPanelGroup.COMPONENT_TYPE);
if(required==true){
setStyleClass(JSFUtils.BootstrapClasses.Rows.toString() + " " +
JSFUtils.BootstrapClasses.Required.toString()
);
}else{
setStyleClass(JSFUtils.BootstrapClasses.Rows.toString());
}
setLayout(JSFUtils.BootstrapClasses.Layout.toString());
setId(id);
}
}
Container is the panel group that holds the component in the page
container=new ContainerFluid(context, app, "SuperPanel");
ContainerFluid
public class ContainerFluid extends HtmlPanelGroup {
public ContainerFluid() {
super();
// TODO Auto-generated constructor stub
}
public ContainerFluid(FacesContext context,Application app,String id) {
app.createComponent(HtmlPanelGroup.COMPONENT_TYPE);
setLayout(JSFUtils.BootstrapClasses.Layout.toString());
setStyleClass(JSFUtils.BootstrapClasses.ContainerFluid.toString());
//compSetId(this, id);
setStyle("padding: 30px;");
//setId(id);
}
}
Inside the xhtml Page there is only :
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ace="http://www.icefaces.org/icefaces/components">
<ui:composition template="/resources/template/master.xhtml">
<ui:define name="content">
<h:panelGroup binding="#{programmaticallyComp.container}"/>
</ui:define>
</ui:composition>
</html>
the problem is in the output of the datatable :
wrong output
and the right output is like this without the expiry date:
right output
we must assign the 'datatable' var to the output label
setDatatableVar("role");
then we must send a value expression to the output label and we must delete the for loop
OutputLabel label1= new OutputLabel(context, app,"#{role.roleNum}");
and we must assign a value expression the OutPutLabel class
private void compSetValue(UIOutput comp, FacesContext context,
Application app, String expression) {
ELContext elContext = context.getELContext();
ValueExpression valueExpression = app.getExpressionFactory()
.createValueExpression(elContext, expression, Object.class);
comp.setValueExpression("value", valueExpression);
}
and the code must be like this
setDatatableVar("role");
OutputLabel label1= new OutputLabel(context, app,"#{role.roleNum}");
OutputLabel label2= new OutputLabel(context, app,"#{role.roleName}");
getColDataTableIdTest().getChildren().add(label1);
getColDataTableNameTest().getChildren().add(label2);
getColDataTableIdTest().getChildren().add(getColDataTableIdTest());
getDataTableTest().getChildren().add(getColDataTableNameTest());
getRowDataTable().getChildren().add(getDataTableTest());
container.getChildren().add(getRowDataTable());
the out put well be like this :
I'm trying to write some simple login page in JSF and I don't really understand the converter class. I have this error when I try to login:
JSF - java.lang.NumberFormatException: For input string: ""
My controller class:
#ManagedBean(name="main")
#SessionScoped
#PersistenceContext(name = "persistence/LogicalName", unitName = "ProjectPU")
public class MainController {
private EntityManager em;
#Resource
private javax.transaction.UserTransaction utx;
#PersistenceUnit(unitName="ProjectPU")
private EntityManagerFactory emf;
/**
* Creates a new instance of MainController
*/
Users user = new Users();
public Users getUser() {
return user;
}
public void setUser(Users user) {
this.user = user;
}
private EntityManager getEntityManager()
{
return emf.createEntityManager();
}
public MainController() {
}
public String signin()
{
EntityManager em = getEntityManager();
TypedQuery<Users> query = em.createNamedQuery("Users.findByLogin", Users.class);
query.setParameter("login", user.getLogin());
Users result = null;
try{
result = query.getSingleResult();
} catch(javax.persistence.NoResultException a){
}
if (result != null) {
if(result.getPassword().equals(user.getPassword())){
Util.getSession().setAttribute("login", user.getLogin());
return "index.xhtml";
}
} else {
return "signin.xhtml";
}
return "index.xhtml";
}
public Object findUser(Integer id) {
EntityManager em = getEntityManager();
try {
Users u = (Users) em.find(Users.class, id);
return u;
} finally {
em.close();
}
}
}
My converter class:
#FacesConverter("Converters.UsersConverter")
public class UsersConverter implements Converter
{
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value)
{
if (!value.equals("null"))
{
Integer id = new Integer(value);
MainController controller = (MainController) context.getApplication().getELResolver().getValue(
context.getELContext(), null, "main");
return controller.findUser(id);
}
else
{
return null;
}
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value)
{
if (value == null) {
return null;
}
if(value instanceof Users)
{
Users u = (Users) value;
return "" + u.getId();
}
else
{
throw new IllegalArgumentException("object:" + value + " of type:" + value.getClass().getName() + "; expected type: Users");
}
}
}
and my view(Index page):
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org /TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
xmlns:h="http://xmlns.jcp.org/jsf/html"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
<h:form>
<table>
<h:inputHidden value="#{main.user}" immediate="true"/>
<tr><td>Login:</td><td><h:inputText id="login" value="# {main.user.login}"/></td></tr>
<tr><td>Password:</td><td><h:inputSecret id="password" value="#{main.user.password}"/></td></tr>
</table>
<h:commandButton id="submit" value="Signin" action="#{main.signin()}"/>
</h:form>
</h:body>
</html>
I created database I have there a table User and I added one user and I cant login. On debugger I can see null values in my converter in both methods(getasobject and getasstring).
this is my xhtml page
<h:form id="order_search" prependId="flase">
<p:growl id="growl" showDetail="true" autoUpdate="true"
sticky="false" />
<h:panelGrid columns="5" style="margin-bottom:10px" cellpadding="5">
<p:outputLabel value="Customer Name : " for="ac_order" />
<p:autoComplete id="ac_order" value="#{orderSearchController.orderFromAutoC}"
completeMethod="#{orderSearchController.autoCompleteOrder}" var="order"
itemLabel="#{order.customerName}" itemValue="#{order}"
converter="#{orderConverter}" forceSelection="true" />
<p:commandButton id="selected" value="print" action="#{orderSearchController.printOrder}" />
</h:panelGrid>
</h:form>
and this is my backing bean
#Component
#ManagedBean
#ViewScoped
public class OrderSearchController implements Serializable{
private static final long serialVersionUID = 1L;
#ManagedProperty(value = "#{orderService}")
public OrderService orderService;
public List<Order> allOrders;
public List<Order> acFilterdOrders;
public Order orderFromAutoC;
#PostConstruct
public void Init() {
System.out.println("init gets called");
// allOrders = new ArrayList<>();
// orderFromAutoC = new Order();
allOrders = orderService.getAllOrders();
System.out.println("After sssssss ");
}
public List<Order> autoCompleteOrder(String query) {
acFilterdOrders = new ArrayList<Order>();
for (int i = 0; i < allOrders.size(); i++) {
if (allOrders.get(i).getCustomerName().toLowerCase().startsWith(query)) {
acFilterdOrders.add(allOrders.get(i));
}
}
return acFilterdOrders;
}
public String printOrder() {
System.out.println("Inside print");
System.out.println("Inside print : "+orderFromAutoC);
return null;
}
//Getters and Setters
}
and this is my converter code
#ManagedBean(name = "orderConverter")
#RequestScoped
public class OrderConverter implements Converter {
#ManagedProperty(value = "#{orderService}")
private OrderService orderService;
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String value) {
if (value != null && value.trim().length() > 0) {
return orderService.getOrderById(Integer.parseInt(value));
} else {
return null;
}
}
#Override
public String getAsString(FacesContext arg0, UIComponent arg1, Object arg2) {
// TODO Auto-generated method stub
return null;
}
public OrderService getOrderService() {
return orderService;
}
public void setOrderService(OrderService orderService) {
this.orderService = orderService;
}
}
the auto-complete component works fine but but when i tried to get the selected value from it in the backing bean it always return a null
The getAsString method is not implemented correctly as it is just returning the NULL value. The return value of getAsString method is passed as value in the getAsObject method based on which it get the value from the list. Here is an example for your reference . Value in the getAsObject method is the Id that is returned from getAsString method.
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
if(value != null && value.trim().length() > 0) {
MessageLogBean messageLogBean = (MessageLogBean) SessionUtility.getManagedBean("messageLogBean");
DeviceResponseDTO deviceResponseDTO = new DeviceResponseDTO();
deviceResponseDTO.setId(Integer.parseInt(value));
List<DeviceResponseDTO> deviceResponseDTOs = messageLogBean.getDeviceResponseDTOs();
int index = deviceResponseDTOs.indexOf(deviceResponseDTO);
if(null != deviceResponseDTOs && !deviceResponseDTOs.isEmpty()){
return deviceResponseDTOs.get(index);
}
return null;
}
else {
return null;
}
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object object) {
if(object != null) {
return String.valueOf(((DeviceResponseDTO) object).getId());
}
else {
return null;
}
}
Hello i am Using PrimeFaces 4.0 and i need to pass object value in SelectOneMenu.
I am using converter to convert that from string format to Class object format.
These are the code files please help me...
lablevalue.xhtml
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:p="http://primefaces.org/ui"
xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>
</h:head>
<h:body>
<h:form id="myform">
<p:growl showDetail="true"></p:growl>
<p:selectOneMenu value="#{itemlableAcction.idCard}" >
<f:converter converterId="converter.SelectMenUConverter" />
<f:selectItem itemLabel="Select" itemValue="" />
<f:selectItems value="#{itemlableAcction.idCards}" var="idv" itemLabel="#{idv.name}" itemValue="#{idv}" />
</p:selectOneMenu>
<h:commandButton action="#{itemlableAcction.onclickSubmit}" value="Submit"></h:commandButton>
</h:form>
</h:body>
</html>
ItemlableAcction
import java.util.ArrayList;
import java.util.List;
import javax.faces.bean.ManagedBean;
import bo.IdCard;
#ManagedBean
public class ItemlableAcction {
List<IdCard> idCards = new ArrayList<IdCard>();
IdCard idCard;
public Object getIdCard() {
return idCard;
}
public void setIdCard(IdCard idCard) {
this.idCard = idCard;
}
public List<IdCard> getIdCards() {
return idCards;
}
public void setIdCards(List<IdCard> idCards) {
this.idCards = idCards;
}
public ItemlableAcction() {
IdCard card1 = new IdCard();
card1.setId(1);
card1.setName("ABC");
card1.setAddress("USA");
idCards.add(card1);
IdCard card2 = new IdCard();
card2.setId(2);
card2.setName("MNO");
card2.setAddress("INDIA");
idCards.add(card2);
IdCard card3 = new IdCard();
card3.setId(3);
card3.setName("XYZ");
card3.setAddress("Chaina");
idCards.add(card3);
}
public String onclickSubmit() {
IdCard ic = (IdCard) idCard;
System.out.println("In action id values are " + ic.getId() + " " + ic.getAddress());
return "";
}
}
SelectMenUConverter
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import bo.IdCard;
#FacesConverter("converter.SelectMenUConverter")
public class SelectMenUConverter implements Converter {
public SelectMenUConverter() {
System.out.println("Inside converter");
}
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
System.out.println("One" + arg2);
IdCard idCard = new IdCard(arg2);
return idCard;
}
public String getAsString(FacesContext arg0, UIComponent arg1, Object value) {
System.out.println("Two" + value);
return value.toString();
}
}
Idcard
public class IdCard {
String name;
int id;
String address;
public IdCard() {
}
public IdCard(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
Imagine you have to implement converter for 100 classes.
If you don't want to implement a own Converter and get the data from exiting list, use:
SelectItems Converter Omnifaces
You have a complete example.
PD: Don't forget to implement toString with a unique id.(See documentation)
You're not searching and getting the required instance from the defined list (in your managed-bean) when getting its identifiant through getAsObject()'s method. You're just instanciating an additional new object through the converter. Try this:
#FacesConverter("converter.SelectMenUConverter")
public class SelectMenUConverter implements Converter {
...
#Override
public Object getAsObject(FacesContext arg0, UIComponent arg1, String arg2) {
if (arg2 == null || arg2.isEmpty()) {return null;}
try {
return findIdCard(arg2); // here's where should be retreived the desired selected instance
} catch (NumberFormatException e) {
throw new ConverterException(new FacesMessage(("This is not a valid card id")), e);
}
}
...
public IdCard findIdCard(String id) {
Iterator<IdCard> iterator = idCards.iterator(); // "idCards" represents your idCards' list. It is not recognized yet in the converter
while(iterator.hasNext()) {
IdCard idc = iterator.next();
if(idc.getId() == Integer.valueOf(id).intValue()) {
return idc;
}
}
return null;
}