PrimeFaces p:autoComplete结合OmniFaces ListConverter报错求助
问题:PrimeFaces AutoComplete 触发 PropertyNotFoundException 错误
开发TCG卡牌搜索表单时,使用PrimeFaces AutoComplete组件实现带自动补全且保持下拉展开的功能,但更新搜索框时出现以下错误:
GRAVE: Error Rendering View[/login.xhtml] javax.el.PropertyNotFoundException: /login.xhtml @46,42 itemLabel="#{card.name}": Propiedad [name] no hallada en el tipo [java.lang.String].
已尝试使用OmniFaces ListConverter,但问题未解决。相关代码如下:
相关代码
XHTML代码
<p:panel header="Card Search" id="searchField"> <p:autoComplete id="cardSearch" value="#{imageGridView.query}" completeMethod="#{imageGridView.completeCards}" var="card" itemLabel="#{card.name}" itemValue="#{card.id}" forceSelection="true" minQueryLength="3" scrollHeight="200" emptyMessage="Cards not found" dropdown="true" queryDelay="500"> <o:converter converterId="omnifaces.ListConverter" list="#{imageGridView.cardsAvailable}" /> <p:column> <h:outputText value="#{card.name}" /> </p:column> <p:ajax event="itemSelect" listener="#{imageGridView.onCardSelect}" update="messages imageGrid searchField" process="@this" oncomplete="keepAutoCompleteOpen()" /> </p:autoComplete> </p:panel>
JavaScript代码
<h:outputScript> function keepAutoCompleteOpen() { var input = PF('cardSearch').input; var query = input.val(); if (query.length >= 3) { PF('cardSearch').search(query); } }</h:outputScript>
Java代码(imageGridView.java)
public List<YGOCard> completeCards(String query) { cardsAvailable=new LinkedList<YGOCard>(); String queryLowerCase = query.toLowerCase(); Controller db=new Controller(); cardsAvailable=db.getYGOCardsByName(queryLowerCase); return cardsAvailable; } public void onCardSelect(SelectEvent<?> event) { if (event.getObject() != null) { imageNames.add(event.getObject().toString()); updateDecklist(); FacesMessage message = new FacesMessage("Success", "Card Added: " + event.getObject().toString()); FacesContext.getCurrentInstance().addMessage(null, message); } }
错误原因分析
这个错误的核心是:组件尝试对java.lang.String类型的对象调用name属性,但String类并没有name方法。出现该问题的关键原因有两点:
value与itemValue类型不匹配:autoComplete的value="#{imageGridView.query}"如果是String类型,而itemValue绑定的是card.id(字符串/数值类型),组件渲染下拉选项时会把value的字符串值当成var="card"的实例,导致调用card.name时出错。- ListConverter使用场景错误:OmniFaces ListConverter适用于
itemValue绑定实体对象本身的场景,但这里itemValue是实体ID而非实体对象,Converter无法将ID正确转换回YGOCard实例,最终card变量变成了ID字符串。
解决方案
1. 统一value与itemValue的类型为实体对象
将autoComplete的value绑定到实体对象,同时修改itemValue为实体本身,确保Converter能正确识别:
- 修改XHTML中的
autoComplete配置:
<p:autoComplete id="cardSearch" value="#{imageGridView.selectedCard}" <!-- 绑定实体对象 --> completeMethod="#{imageGridView.completeCards}" var="card" itemLabel="#{card.name}" itemValue="#{card}" <!-- 绑定实体对象而非ID --> forceSelection="true" minQueryLength="3" scrollHeight="200" emptyMessage="Cards not found" dropdown="true" queryDelay="500"> <o:converter converterId="omnifaces.ListConverter" list="#{imageGridView.cardsAvailable}" /> <!-- 其余代码不变 --> </p:autoComplete>
- 在
imageGridView.java中添加selectedCard属性及getter/setter:
private YGOCard selectedCard; public YGOCard getSelectedCard() { return selectedCard; } public void setSelectedCard(YGOCard selectedCard) { this.selectedCard = selectedCard; }
2. 调整onCardSelect方法逻辑
现在event.getObject()返回的是YGOCard实体,修改方法以获取正确的卡牌信息:
public void onCardSelect(SelectEvent<?> event) { if (event.getObject() != null) { YGOCard selectedCard = (YGOCard) event.getObject(); imageNames.add(selectedCard.getId().toString()); // 存储卡牌ID updateDecklist(); FacesMessage message = new FacesMessage("Success", "Card Added: " + selectedCard.getName()); // 显示卡牌名称 FacesContext.getCurrentInstance().addMessage(null, message); } }
3. 优化下拉展开的JavaScript逻辑
原JS中重复调用search(query)会触发不必要的后端请求,改为直接展开下拉面板:
<h:outputScript> function keepAutoCompleteOpen() { var autoComplete = PF('cardSearch'); var inputValue = autoComplete.jq.val(); if (inputValue.length >= 3 && !autoComplete.panel.is(':visible')) { autoComplete.show(); // 直接展开面板,无需重新查询 } } </h:outputScript>
4. 确保cardsAvailable的作用域正确
在completeCards方法中,不要每次都重新实例化cardsAvailable,直接赋值即可,保证ListConverter能获取到正确的列表:
private List<YGOCard> cardsAvailable; // 实例变量 public List<YGOCard> completeCards(String query) { String queryLowerCase = query.toLowerCase(); Controller db = new Controller(); cardsAvailable = db.getYGOCardsByName(queryLowerCase); // 直接赋值 return cardsAvailable; } // 添加getter方法供Converter调用 public List<YGOCard> getCardsAvailable() { return cardsAvailable; }
内容的提问来源于stack exchange,提问作者Phaleg
相关产品推荐
相关产品推荐

