p:autoComplete itemLabel 抛出“类“java.lang.String”;没有属性“标签”。

发布于 12-08 03:18 字数 1366 浏览 1 评论 0原文

我正在从 IceFaces 更改为 PrimeFaces(我真的很想更改为 RichFaces,但会在新版本中导致错误,我不会),并且我在正确实现 primefaces 自动完成方面遇到了一些困难。根据他的手册,我只需要实现一个返回对象列表的方法,在这种情况下需要一个转换器。

我返回的列表是 javax.faces.model.SelectItem 的列表,我真的不明白为什么我需要为此创建一个转换器,但让我们继续。我创建了一个简单的转换器只是为了测试,但 primefaces 无法识别我的转换器并在浏览器中返回此错误:

/resources/components/popups/popupBuscaPessoa.xhtml @35,41 itemLabel="#{pessoa.label}":类“java.lang.String”没有属性“label”。

这是我的转换器类(只是为了测试):

public class ConversorSelectItem implements Converter {

@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {      
     if (value!=null && value.isEmpty())
         return null;

     SelectItem selectItem=new SelectItem();
     selectItem.setLabel(value);
     return selectItem;     
}

@Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
    return ((SelectItem)object).getLabel();
}
}

这是我尝试使用 p:autocomplete:

<p:autoComplete value="#{modeloPopupBuscaPessoa.itemSelecionado}"
            completeMethod="#{controladorSugestaoPessoa.atualizarSugestoes}"
            var="pessoa" itemLabel="#{pessoa.label}" itemValue="#{pessoa.value}"
            converter="#{conversorSelectItem}"/>

我做错了什么吗? SelectItem 没有默认转换器吗?有没有更简单的方法来实现这个转换器?

I'm changing from IceFaces to PrimeFaces (I really wanted to change to RichFaces but cause a bug in new version, I won't) and I'm havinng some dificults to implement correctly primefaces autoComplete. According to his manual I just need to implement a method that returns a list of objects, and in this case a converter is required.

The list I'm returning is a list of javax.faces.model.SelectItem, I really can't understand why I need to create a converter to this, but lets continue. I've created a simple converter just to test, but primefaces don't recognizes my converter and returns this error in browser:

/resources/components/popups/popupBuscaPessoa.xhtml @35,41 itemLabel="#{pessoa.label}": The class 'java.lang.String' does not have the property 'label'.

This is my conversor class (just to test):

public class ConversorSelectItem implements Converter {

@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {      
     if (value!=null && value.isEmpty())
         return null;

     SelectItem selectItem=new SelectItem();
     selectItem.setLabel(value);
     return selectItem;     
}

@Override
public String getAsString(FacesContext context, UIComponent component, Object object) {
    return ((SelectItem)object).getLabel();
}
}

This is where I try use p:autocomplete:

<p:autoComplete value="#{modeloPopupBuscaPessoa.itemSelecionado}"
            completeMethod="#{controladorSugestaoPessoa.atualizarSugestoes}"
            var="pessoa" itemLabel="#{pessoa.label}" itemValue="#{pessoa.value}"
            converter="#{conversorSelectItem}"/>

Did I do something wrong? Isn't there a default converter for SelectItem? Is there a easier way to implement this converter?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

不甘平庸2024-12-15 03:18:19

您不应该使用 List 为其提供数据。您应该使用 List 为其提供数据。您也不应该专注于转换 SelectItem。您应该集中精力转换项目值,即 PessoaSelectItem 是旧 JSF 1.x 时代的遗留物。在 JSF 2.x 中,由于视图中的 varitemValueitemLabel 属性,这不再是强制性的。这可以让您的 Bean 免受特定于视图的混乱的影响。

仅当您使用 itemValue="#{pessoa}"#{modeloPopupBuscaPessoa.itemSelecionado} 引用 时,才需要 Converter佩索阿属性。然后,您应该在 getAsString() 中将 Pessoa 转换为其唯一的 String 表示形式(以便可以以 HTML 格式打印)并在 getAsObject()String 转换为 Pessoa (以便可以在 bean 属性中设置)。

但是,如果 #{pessoa.value}String 并且 #{modeloPopupBuscaPessoa.itemSelecionado} 也是 String >,那么您应该只使用 itemValue="#{pessoa.value}" 并完全删除 Converter

另请参阅:

You shouldn't feed it with List<SelectItem>. You should feed it with List<Pessoa>. You should also not concentrate on converting SelectItem. You should concentrate on converting the item value, which is Pessoa. The SelectItem is a leftover from the old JSF 1.x ages. In JSF 2.x this is not mandatory anymore, thanks to the var, itemValue and itemLabel attributes in the view. This keeps your bean clean from view-specific clutter.

The Converter is only necessary whenever you use itemValue="#{pessoa}" and the #{modeloPopupBuscaPessoa.itemSelecionado} refers a Pessoa property. You should then in getAsString() convert Pessoa to its unique String representation (so that it can be printed in HTML) and in getAsObject() convert from String to Pessoa (so that it can be set in bean property).

However, if #{pessoa.value} is a String and #{modeloPopupBuscaPessoa.itemSelecionado} is also a String, then you should just use itemValue="#{pessoa.value}" and remove the Converter altogether.

See also:

似梦非梦2024-12-15 03:18:19

一个通用转换器,可用于 Primefaces 自动完成和所有其他目的:

import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.WeakHashMap;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

@FacesConverter(value = "entityConverter")
public class EntityConverter implements Converter {

    private static Map<Object, String> entities = new WeakHashMap<Object, String>();

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object entity) {
        synchronized (entities) {
            if (!entities.containsKey(entity)) {
                String uuid = UUID.randomUUID().toString();
                entities.put(entity, uuid);
                return uuid;
            } else {
                return entities.get(entity);
            }
        }
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
        for (Entry<Object, String> entry : entities.entrySet()) {
            if (entry.getValue().equals(uuid)) {
                return entry.getKey();
            }
        }
        return null;
    }

}

A generic Converter which you can use for Primefaces Auto-completes and all other purposes:

import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.WeakHashMap;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;

@FacesConverter(value = "entityConverter")
public class EntityConverter implements Converter {

    private static Map<Object, String> entities = new WeakHashMap<Object, String>();

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object entity) {
        synchronized (entities) {
            if (!entities.containsKey(entity)) {
                String uuid = UUID.randomUUID().toString();
                entities.put(entity, uuid);
                return uuid;
            } else {
                return entities.get(entity);
            }
        }
    }

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String uuid) {
        for (Entry<Object, String> entry : entities.entrySet()) {
            if (entry.getValue().equals(uuid)) {
                return entry.getKey();
            }
        }
        return null;
    }

}
别挽留2024-12-15 03:18:19

我遇到了同样的问题以及作者的评论 Primefaces autocomplete with POJO and String value< /a> 给了我在我的案例中找到问题根源的提示。

概述

问题在于 value=#{objectValue} 的类型为 String,但 completeMethod 中引用的方法返回一个 List对象>

设计

我有以下 POJO(简化的):

public class CollaboratorGroup {
   private String groupId;

   private String groupName;

   private Collaborator piUserId;

   ...
}

并且

public class Collaborator {
   private String userId;

   private String fullName;

   private String groupId;
   ...
}

这是否是一个有用的设计并不重要。我只是想解决这个问题。

以下p:autoComplete(简化):

<p:autoComplete var="group"
    itemLabel="#{group.groupId}"
    itemValue="#{group.groupId}"
    completeMethod="#{bean.completeGroup}"
    value="#{collaborator.groupId}">
    <f:facet name="itemtip">
        <p:panelGrid columns="2">
            <f:facet name="header">
                <h:outputText value="#{group.groupId}" />
            </f:facet>
            <h:outputText value="Name:" />
            <h:outputText value="#{group.groupName}" />
            <h:outputText value="PI" />
            <h:outputText value="#{group.piUserId.fullName}" />
        </p:panelGrid>
    </f:facet>
</p:autoComplete>

将抛出The class 'java.lang.String' does not have the property 'groupId'。当我更改为 itemLabel=#{group} 时,我会在输入字段中看到 groupId CG00255,但许多 org.coadd.sharedresources.model.CollaboratorGroup @... 在下拉列表中。如果我选择其中之一,则此 toString() 值将设置为 Collaborator.groupId,这是不需要的。

问题的根源

我将 List 提供给 p:autoComplete,而 Collaborator.groupId 是一个 StringitemLabel 用于“格式化”两者,String groupId 设置为 value="#{collaborator.groupId}" 和来自 ListCollaboratorGroup,由 completeMethod="#{bean.completeGroup}" 生成。

可能的解决方案

  1. 如果不会破坏您的模型,您可以通过将 Collaborator 中的成员 groupId 更改为 CollaboratorGroup 来调整 Model。设计。在这种情况下,特别是当 CollaboratorGroup 具有成员 Collaborator piUserId 时。
  2. 您只需用 List填充 p:autoComplete 即可。 groupIdList 但在这种情况下,您必须为 itemtip 找到不同的解决方案。

  3. 一个非常快速的解决方案是使用 itemLabel="#{group.class.simpleName eq 'String' ? group : group.groupId}"Primefaces 使用 POJO 和字符串值自动完成

    • 问题
      • 您必须关心NullPointerExceptions
      • 您用逻辑填充您的View
      • 这不是一个非常灵活或动态的设计。
  4. 在 bean 方法 itemLabel="#{bean.printGroupId(group)}" 中实现 3.,您可以完全控制逻辑。这就是我所做的。

    public String printGroupId(对象组) {
        if (group == null) 返回 null;
        返回(字符串的组实例)? (字符串)组:(CollaboratorGroup 的组实例)? ((CollaboratorGroup) 组).getGroupId() : null;
    }
    

    (不是最好的,只是为了给您一个想法。)

I faced the same issue and the author's comment at Primefaces autocomplete with POJO and String value gave me the hint to find the source of the problem in my case.

Overview

The problem is that value=#{objectValue} is of type String but the method referenced in completeMethod is returning a List<Object>.

The Design

I have the following POJOs (simplified):

public class CollaboratorGroup {
   private String groupId;

   private String groupName;

   private Collaborator piUserId;

   ...
}

and

public class Collaborator {
   private String userId;

   private String fullName;

   private String groupId;
   ...
}

Does not matter whether this is a useful design. I just want to address the issue.

The following p:autoComplete (simplified):

<p:autoComplete var="group"
    itemLabel="#{group.groupId}"
    itemValue="#{group.groupId}"
    completeMethod="#{bean.completeGroup}"
    value="#{collaborator.groupId}">
    <f:facet name="itemtip">
        <p:panelGrid columns="2">
            <f:facet name="header">
                <h:outputText value="#{group.groupId}" />
            </f:facet>
            <h:outputText value="Name:" />
            <h:outputText value="#{group.groupName}" />
            <h:outputText value="PI" />
            <h:outputText value="#{group.piUserId.fullName}" />
        </p:panelGrid>
    </f:facet>
</p:autoComplete>

will throw The class 'java.lang.String' does not have the property 'groupId'. When I change to itemLabel=#{group}, I will see the groupId CG00255 in the input field but many of org.coadd.sharedresources.model.CollaboratorGroup@... in the dropdown list. If I select one of these, this toString() value is set to Collaborator.groupId which is not desired.

The Problem's Source

I feed the p:autoComplete with a List<CollaboratorGroup> while Collaborator.groupId is a String and itemLabel is used to "format" both, the String groupId set as value="#{collaborator.groupId}" and the CollaboratorGroup that comes from the List, generated by completeMethod="#{bean.completeGroup}".

Possible Solutions

  1. You could adjust the Model by changing the member groupId to CollaboratorGroup in Collaborator if it does not destroy your design. In this case, especially as CollaboratorGroup has the member Collaborator piUserId.
  2. You could just fill the p:autoComplete with List<String> groupIdList but in this case you have to find a different solution for the itemtip.

  3. A very quick solution is to use itemLabel="#{group.class.simpleName eq 'String' ? group : group.groupId}" as mentionned at Primefaces autocomplete with POJO and String value.

    • Problems
      • You have to care about NullPointerExceptions.
      • You fill up your View with logic.
      • It is not a very flexible or dynamic design.
  4. Implement 3. in a bean method itemLabel="#{bean.printGroupId(group)}" where you have full control over the logic. This is what I did.

    public String printGroupId(Object group) {
        if (group == null) return null;
        return (group instanceof String) ? (String) group : (group instanceof CollaboratorGroup) ? ((CollaboratorGroup) group).getGroupId() : null;
    }
    

    (Not the best, just to give you an idea.)

音盲2024-12-15 03:18:19

实现这一目标的另一种最简单的方法是:
重写 Pessoa Pojo 类中的 toString() 方法。
这个 toString() 应该只返回您想要显示的标签
如果您使用此方法,则无需转换器

例如:

public class Pessoa implements Serializable{

private String value;
private String label;

//Setter and getters

@Override
public void toString(){
return label;
}

}

那么你可以使用:

<p:autoComplete value="#{modeloPopupBuscaPessoa.itemSelecionado}"
                completeMethod="#{controladorSugestaoPessoa.atualizarSugestoes}"
                var="pessoa" itemLabel="#{pessoa}" itemValue="#{pessoa.value}"/>

这是我目前正在使用并且运行良好的方式。

One more easiest way to achieve this by:
overriding the toString() method in Pessoa Pojo class.
This toString() should only return Label that you want to display.
If you use this method then there is NO NEED for converter.

For example:

public class Pessoa implements Serializable{

private String value;
private String label;

//Setter and getters

@Override
public void toString(){
return label;
}

}

Then you can use:

<p:autoComplete value="#{modeloPopupBuscaPessoa.itemSelecionado}"
                completeMethod="#{controladorSugestaoPessoa.atualizarSugestoes}"
                var="pessoa" itemLabel="#{pessoa}" itemValue="#{pessoa.value}"/>

This is the way i'm currently using and working well.

万劫不复2024-12-15 03:18:19
 ELContext elContext = FacesContext.getCurrentInstance().getELContext();
 ItemBean itemBean = (ItemBean) elContext.getELResolver().getValue(elContext, null, "itemBean");
 for(Item item : itemBean.getItems()){
     if(item.getId().getItemCode().equals(value)){
         return item;
     }
 }
 ELContext elContext = FacesContext.getCurrentInstance().getELContext();
 ItemBean itemBean = (ItemBean) elContext.getELResolver().getValue(elContext, null, "itemBean");
 for(Item item : itemBean.getItems()){
     if(item.getId().getItemCode().equals(value)){
         return item;
     }
 }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文