1

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.

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
BgY
  • 83
  • 9
  • Show us your configuration. Most likely your PartnerSelComp component is in a package that isn't loaded by your configuration. – Jorge Campos Jan 28 '17 at 22:37

1 Answers1

2

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.

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63