How can I use #ManagedProperty in backing component?
This is partner selector composite component. The component checks the typed partner code in the database and fills the partner name if code is valid.
The component:
<cc:interface componentType="partnerSelComp">
<cc:attribute name="value" type="java.lang.Long"/>
</cc:interface>
<cc:implementation>
<span id="#{cc.clientId}" style="white-space:nowrap">
<p:inputText id="id" type="hidden" binding="#{cc.partnerId}"/>
<p:inputText id="code" binding="#{cc.code}">
<p:ajax event="blur" update="id code name" listener="#{cc.validate}" />
</p:inputText>
<p:inputText id ="name" disabled="true" binding="#{cc.name}" />
<p:message for="code"/>
</span>
</cc:implementation>
In encodeBegin() I got NPE because service is null:
#FacesComponent("partnerSelComp")
public class PartnerSelComp extends UIInput implements NamingContainer {
private InputText partnerId;
private InputText code;
private InputText name;
#ManagedProperty("#{partnerService}")
private PartnerService service;
#Override
public void encodeBegin(FacesContext context) throws IOException {
Partner p=null;
Long i = (Long) getValue();
if (i != null) {
p = findPartnerById(service.getList(), i);
}
fill( (i==null) , p); // fills the code and name fields
}
...
}
This is the bean I'd like to access. (later it will replaced with a JPA query.)
#ManagedBean(name = "partnerService")
#ApplicationScoped
public class PartnerService {
private List<Partner> list;
public PartnerService() {
list = new ArrayList<>();
list.add( new Partner(1, "A", "Partner A"));
list.add( new Partner(2, "B", "Partner B"));
list.add( new Partner(3, "C", "Partner C"));
list.add( new Partner(4, "D", "Partner D"));
list.add( new Partner(5, "E", "Partner E"));
list.add( new Partner(6, "E", "Partner F"));
}
public List<Partner> getList() {
return list;
}
public void setList(List<Partner> list) {
this.list = list;
}
}
The solution:
The use of the component:
<my:PartnerSelComp value="#{myBean.partnerId}" service="#{partnerService}"/>
The component xhtml:
<cc:interface componentType="partnerSelComp">
<cc:attribute name="value" type="java.lang.Long"/>
<cc:attribute name="service"/>
</cc:interface>
<cc:implementation>
<span id="#{cc.clientId}" style="white-space:nowrap">
<p:inputText id="id" type="hidden" binding="#{cc.partnerId}"/>
<p:inputText id="code" binding="#{cc.code}">
<p:ajax event="blur" update="id code name" listener="#{cc.validate}" />
</p:inputText>
<p:inputText id ="name" disabled="true" binding="#{cc.name}" />
<p:message for="code"/>
</span>
</cc:implementation>
I note, I tried it to pass the reference as attribute default value: <cc:attribute name="service" default="#{partnerService}"/> and <my:PartnerSelComp value="#{myBean.partnerId}"/> I don't know why but it didn't worked me, I had to set service attribute in my:PartnerSelComp as you see above.
And the backing component:
#FacesComponent("partnerSelComp")
public class PartnerSelComp extends UIInput implements NamingContainer {
private InputText partnerId;
private InputText code;
private InputText name;
#ManagedProperty("#{partnerService}")
private PartnerService service;
#Override
public void encodeBegin(FacesContext context) throws IOException {
Partner p=null;
Long i = (Long) getValue();
PartnerService service = getAttributeValue("service", null );
if (i != null) {
p = findPartnerById(service.getList(), i);
}
fill( (i==null) , p); // fills the code and name fields
}
#SuppressWarnings("unchecked")
private <T> T getAttributeValue(String key, T defaultValue) {
T value = (T) getAttributes().get(key);
return (value != null) ? value : defaultValue;
}
...
}
I have to use getAttributes().get(key) to get the reference and cast it to PartnerService.
Thanks for the answers.
Try to load it using the html configuration interface:
<cc:interface componentType="partnerSelComp">
<cc:attribute name="value" type="java.lang.Long"/>
<cc:attribute name="service" default="#{partnerService}"/>
</cc:interface>
This is mostly for the usage inside of html implementation as inside of the bean class you would have to get it manually anyway:
FacesContext.getCurrentInstance().getAttributes().get("service");
Regarding the direct injection into #FacesComponent it is not possible until next version of JSF (2.3).
A possible workaround would be to use "#Named instead of #FacesComponent or if you cannot do that, then try out some of the features of the http://omnifaces.org/ library. It enables injection into #FacesConverter so maybe you could also apply it for this annotation.
Related
I have a form where I create a user. In my form, I have multiple properties for that user (I actually use a User object for the retainment of data on submit to the backing bean)
create.xhtml
<h:form>
<h:outputLabel for="user_name" value="Name:" />
<h:inputText id="user_name" value="#{createUserView.newUser.username}" />
<br/><br/>
<h:outputLabel for="user_password" value="Default Password*:" />
<h:inputSecret id="user_password" value="#{createUserView.newUser.password}"></h:inputSecret><br/><br/>
<h:outputLabel for="user_organization" value="Organization:" />
<h:selectOneMenu id="user_organization" disabled="true" value="#{createUserView.newUser.organizationId}">
<f:selectItems
value="#{organizationBean.allOrganizations}"
var="org"
itemLabel="#{org.organizationName}"
itemValue="#{org.id}" />
</h:selectOneMenu><br/><br/>
<h:commandButton value="Create" action="#{createUserView.createNewUser}" />
</h:form>
CreateUserView
#ManagedBean(name = "createUserView")
#RequestScoped
public class CreateUserView {
private UserServices userSerivces;
private User newUser;
#ManagedProperty(value="#{organizationBean}")
private OrganizationBean organizationBean;
public CreateUserView() {
newUser = new User();
userSerivces = new UserServices();
}
public void createNewUser() {
userSerivces.createNewUser(newUser);
}
// Getters and Setters
}
OrganizationBean
#ManagedBean(name = "organizationBean")
#RequestScoped
public class OrganizationBean {
private List<Organization> allOrganizations;
private OrganizationServices orgServices;
public OrganizationBean() {
orgServices = new OrganizationServices();
allOrganizations = orgServices.retrieveAllOrganizations();
}
// Getters and Setters
}
The issue here is that when I reference the newUser object in the backing bean, the organizationId value is null.
I assume this is because OrganizationBean (excuse the confusing in naming, refactoring) is either not rendered for my current view or I need to somehow inject.
I've tried a managed property in the CreateUserView backing bean that references the OrganizationBean, but no luck. The organizationID value in the newUser object is null.
Do I need to populate a list in the CreateUserView bean using the OrganizationBean injection, so that it has it's own list it can render?
What am I missing? Feeling foolish.
JSF 2.0
The problem, as stated in the comments is that you don't have a Converter for your Organization class.
You must have it in order to know what Organization matches every SelectItem. The converter must be something like:
#FacesConverter(forClass = Organization.class, value = "organizationConverter")
public class OrganizationConverter implements Converter
{
#Override
public Object getAsObject(FacesContext fc, UIComponent uic, String id)
{
if (StringUtils.isEmpty(id))
{
return null;
}
// Convert id to an Organizacion
return organization;
}
#Override
public String getAsString(FacesContext fc, UIComponent uic, Object o)
{
if (o instanceof Organization)
{
return ...;//Convert organization to id
}
return null;
}
}
And then in your selectonemenu:
<h:selectOneMenu id="user_organization" disabled="true" value="#{createUserView.newUser.organizationId}"
converter="organizationConverter">
I am trying to pass a parameter from one page to another, but when i press the button "Details" it gives me this error: "Unable to create managed bean agencyDetailBean. The following problems were found: - The scope of the object referenced by expression #{agency}, request, is shorter than the referring managed beans (agencyDetailBean) scope of session"
This is part from my .xhtml page:
<p:dataGrid var="agency" value="#{agencyBean.agencyList}" columns="1"
rows="5" paginator="true" paginatorPosition="bottom">
<p:growl id="growl" showDetail="true" />
<p:fieldset legend="${agency.tUser.name}" toggleable="true">
<p:ajax event="toggle" listener="#{fieldsetBean.handleToggle}"
update="growl" />
<h:panelGrid columns="2" cellpadding="3" cellspacing="1">
<h:panelGrid columns="2" cellpadding="3" cellspacing="1">
Name: <h:outputText value="${agency.tUser.name}" />
Phone: <h:outputText value="${agency.tUser.phone}" />
</h:panelGrid>
<h:commandButton value="Details" action="agencyDetail">
<f:setPropertyActionListener target="#{agencyBean.tAgency}"
value="${agency}" />
</h:commandButton>
</h:panelGrid>
</p:fieldset>
</p:dataGrid>
My 1st bean:
#ManagedBean(name = "agencyBean")
#SessionScoped
public class AgencyBean implements Serializable {
private TAgency tAgency = new TAgency();
private List<TAgency> agencyList;
public List<TAgency> getAgencyList() {
return agencyList;
}
public void setAgencyList(List<TAgency> agencyList) {
this.agencyList = agencyList;
}
#PostConstruct
public void init() {
EntityManager em = HibernateUtil.getEntityManager();
Query q = em.createQuery("select u from TAgency u");
agencyList = q.getResultList();
}
public TAgency gettAgency() {
return tAgency;
}
public void settAgency(TAgency tAgency) {
this.tAgency = tAgency;
}
}
My 2nd bean class:
#ManagedBean(name = "agencyDetailBean", eager = true)
#SessionScoped
public class AgencyDetailBean implements Serializable {
private static final long serialVersionUID = 1L;
#ManagedProperty(value = "#{tAgency}")
private AgencyBean agencyBean;
private TAgency tAgency;
public TAgency gettAgency() {
if (agencyBean != null) {
tAgency = agencyBean.gettAgency();
}
return tAgency;
}
public void setAgencyBean(AgencyBean agency) {
this.agencyBean = agency;
}
}
Initially, the .xhml page was like this and it worked.
<p:growl id="growl" showDetail="true" />
<c:forEach items="#{agencyBean.agencyList}" var="agency">
<p:fieldset legend="${agency.tUser.name}" toggleable="true">
...
<h:commandButton value="Details" action="agencyDetail">
<f:setPropertyActionListener target="#{agencyDetailBean.agency}"<br/>
value="${agency}" />
</h:commandButton>
</h:panelGrid>
</p:fieldset>
</c:forEach>
Any suggestion what is the problem and how to fix it?
Thanks in advance!
Using #ManagedBean, you cannot inject beans with smaller scope into beans with bigger scope. For your case, inject the #SessionScoped into the #RequestScoped. Then you can update any properties of the #SessionScoped from the #RequestScoped bean.
Regarding your update, in the 2nd bean, you cannot get tAgency from a getter like that. The following function would do the job.
#PostConstruct
public void init() {
tAgency = agencyBean.gettAgency();
}
In my code, I have a PrimeFaces' wizard component with several tabs as following:
<h:form id="myForm">
<p:wizard flowListener="#{mrBean.flowControl}" widgetVar="wiz">
<p:tab id="tab1"></p:tab>
<p:tab id="tab2"></p:tab>
<p:tab id="tab3">
<h:selectOneMenu id="couponList" value="#{mrBean.coupon}"
converter="#{codeToCouponConverter}" >
<f:ajax listener="#{mrBean.doSomething}" execute="#this"/>
<f:selectItem noSelectionOption="true" itemLabel="Choose one..." />
<f:selectItems value="#{mrBean.coupons}" var="c"
itemValue="#{c}" itemLabel="#{c.name} - $ #{c.discount}" />
</h:selectOneMenu>
</p:tab>
</p:wizard>
</h:form>
This is the code for the managed bean:
#ManagedBean(name = "mrBean")
#ViewScoped
public class MrBean {
private List<Coupon> coupons;
private Coupon coupon;
public void doSomething() {
System.out.println("DONE");
}
public String flowControl(FlowEvent event) {
...
}
// Getters and Setters
}
In 1 of the tab, I have a <h:selectOneMenu> component which contains a <f:ajax> tag. I have no idea why the listener is only triggered when I choose the Choose one... option. When I choose any other options from the mrBean.coupons list, the listener is never triggered. In other words, I never saw any DONE printed on the console.
*UPDATE***: The problem turns out to be coming from the following Converter:
#RequestScoped
#ManagedBean
public class CodeToCouponConverter implements Converter {
#EJB
private MrsBeanInterface mrsBean;
#Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
String couponCode = value;
if (value != null) return mrsBean.getCoupon(couponCode);
else return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
if (value != null) {
Coupon c = (Coupon) value;
return c.getId();
} else return null;
}
// Getters and Setters
public MrsBeanInterface getMrsBean() {
return mrsBean;
}
public void setMrsBean(MrsBeanInterface mrsBean) {
this.mrsBean = mrsBean;
}
}
If I change the <h:selectOneMenu> as following:
<h:selectOneMenu id="couponList" value="#{mrBean.couponCode}" >
<f:ajax listener="#{mrBean.doSomething}" execute="#this"/>
<f:selectItem noSelectionOption="true" itemLabel="Choose one..." />
<f:selectItems value="#{mrBean.coupons}" var="c"
itemValue="#{c.id}" itemLabel="#{c.name} - $ #{c.discount}" />
</h:selectOneMenu>
and update the mrBean.doSomething function as following:
#EJB
private MrsBeanInterface mrsBean;
private String couponCode;
private Coupon coupon;
public void doSomething() {
this.coupon = mrsBean.getCoupon(couponCode);
System.out.println("DONE");
}
everything works perfectly.
I would be very grateful if you could give me an explanation of what I have done wrong with the Converter.
Best regards,
James Tran
Use #{mrBean.doSomething()} with the braces or add the event parameter to the method.
I've got a JSF creation form using a selectManyListbox with selectItems inside (it should contain all tags available for projects). The selectItems list though is always empty - when the page renders, there's nothing in the Listbox. Still, the list in the backing bean contains 3 entries (checked that). What am I doing wrong?
The backing bean:
#ManagedBean(name = "createProjectBean")
#RequestScoped
public class CreateProjectBean {
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
private Project project;
private IProjectService projectService;
private FacesContext facesContext;
private MessageFactory mf;
private List<Tag> tags;
public CreateProjectBean() {
project = new Project();
projectService = (IProjectService)ServiceFinder.getInstance()
.findBean("projectService");
mf = new MessageFactory("properties.projects.messages");
tags = projectService.getTags();
}
/* should be the source of tags */
public void setTags(List<Tag> tags) {
this.tags = tags;
}
public List<Tag> getTags() {
return tags;
}
}
And the page:
<f:view>
<h:outputText id="error" rendered="false" />
<h:message styleClass="errorMessage" for="error" />
<h:form id="creationForm" >
<h:panelGrid columns="2" width="420">
/* blah, blah, set name and stuff */
<h:selectManyListbox id="box" value = "#{createProjectBean.project.tags}">
<f:converter converterId="tag" />
<f:selectItems value="#{createProjectBean.tags}"
var="tag"
itemValue="#{tag}"
itemLabel="${tag.name}" />
</h:selectManyListbox>
<f:verbatim><br/></f:verbatim>
<h:commandButton value="Create" styleClass="formButton" action="#{createProjectBean.create}"/>
</h:panelGrid>
</h:form>
</f:view>
I tried to do it per analogia to this page:
http://digitaljoel.wordpress.com/2010/01/11/jsf-2-custom-converter/
The converter I've written is yet to be tested.
Your EL is bogus. You should use the #{} notation everywhere. Replace
<f:selectItems value="#{createProjectBean.tags}"
var="tag"
itemValue="#{tag}"
itemLabel="${tag.name}" />
by
<f:selectItems value="#{createProjectBean.tags}"
var="tag"
itemValue="#{tag}"
itemLabel="#{tag.name}" />
Strange error i received from compiler:
Expected a child component type of UISelectItem/UISelectItems for component type javax.faces.SelectOne(siachoice). Found javax.faces.component.UISelectItems.
So, if he was expecting UISelectItems, and found UISelectItems, then where is the error?
My JSP implementation:
<h:selectOneMenu id="siachoice" value="#{dbSelectBean.currentOption}">
<f:selectItems value="#{dbSelectBean.dbs}" />
</h:selectOneMenu>
Method, where i am setting UISelectItem to UISelectItems:
private UISelectItems populateDatabases(String databaseString) {
UISelectItems selects = new UISelectItems();
List<UISelectItem> result = new ArrayList<UISelectItem>();
StringTokenizer tokeniz = new StringTokenizer(databaseString, GlobalConstants.DELIMITER);
while(tokeniz.hasMoreTokens()){
String tempDB = tokeniz.nextToken();
UISelectItem item = new UISelectItem();
item.setItemValue(tempDB);
item.setItemLabel(tempDB);
result.add(item);
}
selects.setValue(result);
return selects;
}
Then, of course, i am setting it to the bean variable dbs.
Help?
You must return a Collection of javax.faces.model.SelectItem
List list = new ArrayList();
list.add(new SelectItem(value, label));
return list;
The <f:selectItems value="#{bean.items}" /> expects one of the following values:
public SelectItem[] getItems() {}
public List<SelectItem> getItems() {}
public Map<String, Object> getItems() {}
The commonly used one is indeed the List<SelectItem>.
Edit: as response to the comment: UISelectItem represents the <f:selectItem> component. The same applies to UISelectItems and <f:selectItems>. E.g.
<f:selectItem binding="#{bean.selectItem}" />
<f:selectItems binding="#{bean.selectItems}" />
which are bound as
private UISelectItem selectItem;
private UISelectItems selectItems;
// +getter +setter
this way you can control the components programmatically -as for every other UIComponent.
<h:form>
<h:selectOneListbox size="5" >
<f:selectItems value="#{userManager.Test()}" />
</h:selectOneListbox>
</h:form>
import javax.faces.model.SelectItem;
import tn.com.ttnproject.entity.*;
#Name("userManager")
#Scope(ScopeType.CONVERSATION)
public class UserManager {
public List <SelectItem> listAllUsersNames;
SelectItem element;
public List<SelectItem> Test(){
listAllUsersNames = new ArrayList<SelectItem>();
for (int i=1;i<=10;i++)
{
element=new SelectItem(
new Integer(i),
i+".00 euros",
"Produit à "+i+".00 euros");
listAllUsersNames.add(element);
}
return listAllUsersNames;
}
}
The problem is UISelectItem is a component clas so it has to be paired with jsf tag by binding attribute. If you want to have pure values you have to use SelectItem(s) classes.